#include <jni.h>
#include <android/log.h>
#include <libavformat/avformat.h>
#include <android/native_window_jni.h>
#include <android/native_window.h>
#include <libavutil/imgutils.h>
#include <libswscale/swscale.h>
#include <unistd.h>

#define LOGI(FORMATTER, ...) __android_log_print(ANDROID_LOG_INFO,"cherry_ndk_file",FORMATTER,__VA_ARGS__)
#define LOGE(FORMATTER, ...) __android_log_print(ANDROID_LOG_ERROR,"cherry_ndk_file",FORMATTER,__VA_ARGS__)


JNIEXPORT void JNICALL
Java_com_cherry_ndkdemo_FFmpegUtils_render(JNIEnv *env, jclass clazz, jstring jvideo_path,
                                           jobject jsurface) {

    const char *video_path = (*env)->GetStringUTFChars(env, jvideo_path, NULL);
    //注册所有组件
    av_register_all();

    AVFormatContext *pFormatContext = avformat_alloc_context();
    //打开视频文件
    if (avformat_open_input(&pFormatContext, video_path, NULL, NULL) != 0) {
        LOGE("不能打开文件:%s", video_path);
        return;
    }

    if (avformat_find_stream_info(pFormatContext, NULL) < 0) {
        LOGE("无法获取视频文件%s信息", video_path);
        return;
    }

    //获取视频流的索引位置
    //遍历所有类型的流（音频流、视频流、字幕流），找到视频流
    int videoStream = -1, i;
    for (int i = 0; i < pFormatContext->nb_streams; ++i) {
        if (pFormatContext->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO
            && videoStream < 0) {
            videoStream = i;
            break;
        }
    }
    if (videoStream == -1) {
        LOGE("找不到视频流=%s", video_path);
        return;
    }

    //只有知道视频的编码方式，才能够根据编码方式去找到解码器
    //获取视频流中的编解码上下文
    AVCodecContext *pCodecContext = pFormatContext->streams[videoStream]->codec;
    AVCodec *pCodec = avcodec_find_decoder(pCodecContext->codec_id);
    if (pCodec == NULL) {
        LOGE("%s", "找不到解码器");
        return;
    }
    if (avcodec_open2(pCodecContext, pCodec, NULL) < 0) {
        LOGE("解码器无法打开文件=%s", video_path);
        return;
    }

    int width = pCodecContext->width;
    int height = pCodecContext->height;

    //输出视频信息
    LOGI("视频的文件格式：%s", pFormatContext->iformat->name);
    LOGI("视频时长：%lld", (pFormatContext->duration) / 1000000);
    LOGI("视频的宽高：%d,%d", width, height);
    LOGI("解码器的名称：%s", pCodec->name);

    //获取Naive window
    ANativeWindow *nativeWindow = ANativeWindow_fromSurface(env, jsurface);
    // 设置native window的buffer大小,可自动拉伸
    ANativeWindow_setBuffersGeometry(nativeWindow, width, height, WINDOW_FORMAT_RGBA_8888);

    //动态分配帧内存
    AVFrame *pFrame = av_frame_alloc();
    //动态分配渲染内存
    AVFrame *pFrameRGBA = av_frame_alloc();
    if (pFrame == NULL || pFrameRGBA == NULL) {
        LOGE("%s", "不能动态分配内存");
        return;
    }
    //buffer中数据就是用于渲染的,且格式为RGBA
    int numBytes = av_image_get_buffer_size(AV_PIX_FMT_RGBA, width, height, 1);
    //typedef unsigned char __uint8_t;
    uint8_t *buffer = (uint8_t *) av_malloc(numBytes * sizeof(uint8_t));

    av_image_fill_arrays(pFrameRGBA->data, pFrameRGBA->linesize,
                         buffer, AV_PIX_FMT_RGBA, width, height, 1);



    // 由于解码出来的帧格式不是RGBA的,在渲染之前需要进行格式转换
    /**
     * 成功后返回SwsContext 类型的结构体。
参数1：被转换源的宽
参数2：被转换源的高
参数3：被转换源的格式，eg：YUV、RGB……（枚举格式，也可以直接用枚举的代号表示eg：AV_PIX_FMT_YUV420P这些枚举的格式在libavutil/pixfmt.h中列出）
参数4：转换后指定的宽
参数5：转换后指定的高
参数6：转换后指定的格式同参数3的格式
参数7：转换所使用的算法
     SWS_FAST_BILINEAR 228 图像无明显失真，感觉效果很不错。
     SWS_BILINEAR 95 感觉也很不错，比上一个算法边缘平滑一些。
     SWS_BICUBIC 80 感觉差不多，比上上算法边缘要平滑，比上一算法要锐利。
参数8：NULL
参数9：NULL
参数10：NULL
     */
    struct SwsContext *sws_ctx = sws_getContext(width, height, pCodecContext->pix_fmt,
                                                width, height, AV_PIX_FMT_RGBA,
                                                SWS_BILINEAR, NULL, NULL, NULL);


    ANativeWindow_Buffer nativeWindowBuffer;
    int frameFinished;
    AVPacket packet;
    while (av_read_frame(pFormatContext, &packet) >= 0) {
        if (packet.stream_index == videoStream) {

            avcodec_decode_video2(pCodecContext, pFrame, &frameFinished, &packet);
            // 并不是decode一次就可解码出一帧
            if (frameFinished) {
                ANativeWindow_lock(nativeWindow, &nativeWindowBuffer, 0);
                //格式转换
                sws_scale(sws_ctx, (uint8_t const *const *) pFrame->data, pFrame->linesize, 0,
                          height, pFrameRGBA->data, pFrameRGBA->linesize);

                uint8_t *dst = (uint8_t *) nativeWindowBuffer.bits;
                int dstStride = nativeWindowBuffer.stride * 4;
                uint8_t *src = pFrameRGBA->data[0];
                int srcStride = pFrameRGBA->linesize[0];

                // 由于window的stride和帧的stride不同,因此需要逐行复制
                int h;
                for (h = 0; h < height; ++h) {
                    memcpy(dst + h * dstStride, src + h * srcStride, srcStride);
                }

                ANativeWindow_unlockAndPost(nativeWindow);
                //延时绘制，否则视频会快速播放
                usleep(1000 * 16);
            }
        }
        av_packet_unref(&packet);
    }
    sws_freeContext(sws_ctx);
    av_free(buffer);
    av_free(pFrameRGBA);
    av_free(pFrame);
    avcodec_close(pCodecContext);
    avformat_close_input(&pFormatContext);
    (*env)->ReleaseStringUTFChars(env, jvideo_path, video_path);


}